/**
 *工具库函数：
 *  注：本工具库函数针对性较强，未做多种情况下的容错性处理，不宜用作公共工具库函数
 *
 *@author：ppfyang(hzyang_fan@corp.netease.com)
 *@version: 0.1.0
 *@update: 2014-12-18
 */

(function (global, undefined) {

    // 标记缓存唯一ID
    var __uuid = 0;

    // template缓存
    var __tplCache = {};

    // 标记当前页面hash值
    var __pageHash = window.location.hash;

    // 存入templates后对应的hash管理器
    var __hashReservoir = {};

    // template缓存绑定在对应元素Element上 标识对应元素
    var __expando = '_$$p' + new Date().getTime();

    // 防止有很长空白的class注入
    var __blank = /[\t\r\n\f]/g;

    /**
     *字符串前后空白去除
     * @return {String}         - 去除空白后的字符串
     *
     */
    String.prototype._$trim = function () {
        return this.replace(/(^\s*)|(\s*$)/g, '');
    }

    /**
     *原生拓展：当前函数this拓展
     * 方法转自：nej
     *
     * @param  {Object}    arg0 - 函数内this
     * @return {Function}       - 绑定后的函数
     *
     */
    Function.prototype._$bind = function () {
        var
            __r = [], // nej兼容
            __args = arguments, // 获取参数
            __object = arguments[0], // 获取目标
            __function = this; // this赋值

        // 参数绑定
        return function () {
            var __arg = __r.slice.call(__args, 1);
            __r.push.apply(__arg, arguments);
            return __function.apply(__object || null, __arg);
        };
    }

    /**
     *原生拓展：数组遍历 return false中断
     * @param  {Function}  arg0 - 回调函数
     * @param  {Object}    arg1 - 回调函数内this 可空
     *
     * ```javascript
     * [1,2,3,4,5]._$(function (value, index, self) {
     *     //something
     *     if(index == 3) return false;
     * })
     * ```
     *
     */
    Array.prototype._$forEach = function (_callback, _thisArg) {
        if (this == null || {}.toString.call(_callback) != '[object Function]') {
            return false;
        }
        for (var i = 0, __len = this.length; i < __len; i++) {
            if (_callback.call(_thisArg, this[i], i, this) === false) break;
        }
    }

    /**
     *原生拓展：html 模版注入
     * @param  {Object}    arg0 - 模版中所需要的data
     * @return {String}         - 根据设定编译好的html模版
     *
     * ```html
     * <textarea style="display:none;">
     *     <p>{{traffic}}</p>
     * </textarea>
     * ```
     * ```javascript
     * var data = {traffic : "网商路599号"};
     * var htmlTemp = document.getElementsByTagName(“textarea”).value;
     * xxx.innerHTML = htmlTemp._$inject(data);
     * ```
     *
     */
    String.prototype._$inject = function (obj) {
        return this.replace(/{{(\w+)}}/gi, function (matchs, key) {
            var __result = obj[key];
            if (__result == undefined) {
                throw new Error('Object has no such key: ' + key);
            } else {
                return __result;
            }
        });
    }

    /**
     *原生拓展：定制时间格式
     * 方法转自：http://www.cnblogs.com/atree/archive/2011/06/30/JS-JavaScript-Date-Format.html
     *
     * @param  {String}    arg0 - 定义时间格式
     * @return {String}         - 时间格式对应的字符串
     *
     * ```javascript
     * var _d = new Date();
     * alert(_d._$format('yyyy-MM-dd')); // 2014-12-18
     *```
     *
     */
    Date.prototype._$format = function (fmt) {
        var o = {
            "M+": this.getMonth() + 1, //月份
            "d+": this.getDate(), //日
            "h+": this.getHours(), //小时
            "m+": this.getMinutes(), //分
            "s+": this.getSeconds(), //秒
            "q+": Math.floor((this.getMonth() + 3) / 3), //季度
            "S": this.getMilliseconds() //毫秒
        };

        if (/(y+)/.test(fmt)) {
            fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
        }

        for (var k in o) {
            if (new RegExp("(" + k + ")").test(fmt)) {
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
            }
        }

        return fmt;
    }

    // 工具库私有函数：类型判断
    // @param  {String}    arg0 - 需构造的判断的类型字符串
    // @return {Function}       - 对应的类型判断函数
    function __isType(type) {
        return function (obj) {
            return {}.toString.call(obj) == '[object ' + type + ']';
        }
    }

    // 工具库私有函数：设定页面缓存
    // @param  {Element}   arg0 - 模板插入的目标元素
    // @param  {String}    arg1 - 模板的名称
    // @param  {String}    arg2 - 模板数据 为空时 返回对应模板数据
    // @return {String}         - 在有模板注入的元素下对应的模板元素 未存储时返回false
    function __tplData(elem, name, data) {

        if (!!elem.nodeType && !!name) {

            var __id = elem[__expando];

            // 设置模板缓存
            if (data) {

                if (!__id) {
                    __id = elem[__expando] = __uuid++;
                }

                if (!__tplCache[__id]) {
                    __tplCache[__id] = {};
                }
                __tplCache[__id][name] = data;

            // 获取模板缓存
            } else {

                if (!__id) {
                    return false;
                } else {
                    return __tplCache[__id][name];
                }
            }
        }
    }

    var _$$p = function (selector) {
        return new _$$p.__fn.__init(selector);
    }

    // 空函数
    _$$p._$noop = function () {}

    // @return {Boolean}        - 是否是object
    _$$p._$isObject = __isType('Object');

    // @return {Boolean}        - 是否是string
    _$$p._$isString = __isType('String');

    // @return {Boolean}        - 是否是array
    _$$p._$isArray = Array.isArray || __isType('Array');

    // @return {Boolean}        - 是否是function
    _$$p._$isFunction = __isType('Function');

    // 工具库私有函数：json转换
    // @param  {String}    arg0 - XHR responseText
    function __parseJSON(data) {

        if (window.JSON && window.JSON.parse) {
            return window.JSON.parse(data);
        }

        if (data === null) {
            return data;
        }

        if (_$$p._$isString(data)) {

            data = data._$trim();

            if (data) {
                return ( new Function('return ' + data) )();
            }
        }

        throw new Error('Invalid JSON: ' + data);
    }

    // 工具库私有函数：ajax 工厂构造
    var __ajaxHandler = function () {}

    __ajaxHandler.prototype = {

        // ajax 请求
        __request: function (config) {
            if (!!config.url) {

                // ajax请求方法
                var __method = config.method || 'GET';

                // ajax请求地址
                var __url = config.url;

                // ajax post数据
                var __data = config.data || null;

                // 回调类型
                var __dataType = config.dataType || 'JSON';

                // 请求成功回调函数
                var __success = config.success || _$$p._$noop;

                // 请求失败回调函数
                var __error = config.error || _$$p._$noop;

                // 创建xhr对象
                var __xhr = this.__createXhrObject();

                // 请求状态变化回调
                __xhr.onreadystatechange = function () {

                    if (__xhr.readyState !== 4) return;

                    // 响应内容
                    var __responseData = __dataType == 'JSON' ? __parseJSON(__xhr.responseText) : __xhr.responseText;

                    // 执行回调
                    (__xhr.status === 200) ? __success(__responseData) : __error(__xhr.status);
                };

                // 开始请求
                __xhr.open(__method, __url, true);
                if (__method !== 'POST') __data = null;
                __xhr.send(__data);
            }
        },

        // ajax创建
        __createXhrObject: function () {

            // xhr兼容
            var __methods = [
                function () {
                    return new XMLHttpRequest();
                },
                function () {
                    return new ActiveXObject('Msxml2.XMLHTTP');
                },
                function () {
                    return new ActiveXObject('Microsoft.XMLHTTP');
                }
            ];

            // 获取当前浏览器XHR对象
            for (var i = 0, l = __methods.length; i < l; i++) {
                try {
                    __methods[i]();
                } catch (e) {
                    continue;
                }
                this.__createXhrObject = __methods[i];
                return __methods[i]();
            }
            throw new Error('Could not create an XHR object');
        }

    }

    // 工具库公有函数：AJAX
    // @param  {Object}   arg0 - ajax请求配置
    _$$p._$ajax = function (config) {
        if (_$$p._$isObject(config)) {
            return new __ajaxHandler().__request(config);
        } else {
            throw new Error('Ajax parameter error');
        }
    }

    // 工具库公有函数：设定/获取当前页面的hash值
    // @param  {String}   arg0 - 设定的hash值 为空时返回当前页面的hash值
    _$$p._$pageHash = function (value) {

        // 修改当前页面hash值
        if (value != undefined) {

            window.location.hash = __pageHash = value;

        // 无参数 返回当前页面hash值
        } else {
            return __pageHash;
        }
    }

    // 工具库私有函数：检测hash变化
    // @param  {Function}  arg0 - hash变化后的回调函数
    function __hashChange(callback) {

        if (_$$p._$isFunction(callback)) {

            // 浏览器支持onhashchange事件
            if (('onhashchange' in window) && ((typeof document.documentMode === 'undefined') || document.documentMode == 8)) {

                window.onhashchange = function () {
                    _$$p._$pageHash(window.location.hash);
                    callback();
                }

            // 浏览器不支持hash检测
            } else {

                setInterval(function () {

                    // 标记hash是否改变
                    var hashchanged = (window.location.hash != _$$p._$pageHash());

                    if (hashchanged) {
                        _$$p._$pageHash(window.location.hash);
                        callback();
                    }
                }, 150);
            }
        } else {
            throw new Error('Hashchange parameter error')
        }
    }

    // 工具库公有函数：dom ready callback
    // @param  {Function}  arg0 - dom ready后的回调函数
    _$$p._$domReady = function (callback) {

        if (document.addEventListener) {

            document.addEventListener("DOMContentLoaded", callback(), false);
        // IE
        } else if (document.attachEvent) {

            document.attachEvent("onreadystatechange", function () {
                if (document.readyState === "complete")
                    callback();
            });
        }
    }

    // 工具库公有函数：字符串转节点  注：未做容错处理 当前仅支持唯一子节点
    // @param  {String}   arg0 - 封闭的字符串节点
    // @return {Element}       - 节点元素
    _$$p._$parseHTML = function (elemStr) {

        var __cnt = document.createElement('div');

        __cnt.innerHTML = elemStr._$trim();

        return __cnt.childNodes[0];
    }

    _$$p.__fn = _$$p.prototype = {

        // _$$p.__fn构造函数
        constructor: _$$p,

        // 简易id class选择器
        // @param  {String}   arg0 - 选择器
        // @param  {Element}  arg0 - 选择器
        // @return {Object}        - this 链式调用
        __init: function (selector, target) {

            // 选择器存储
            var __dom = [];

            // 元素下查找
            var __target = target || document;

            if (_$$p._$isString(selector)) {

                // html5选择器
                if (!!document.querySelectorAll && document.documentMode != 8) {

                    __dom = [].slice.call(__target.querySelectorAll(selector));

                // 通用选择器
                } else {

                    // 检测是否为ID选择器
                    var __idSelector = /#(.+)/;

                    // 检测是否为CLASS选择器
                    var __classSelector = /\.(.+)/;

                    if (!!__idSelector.test(selector)) {

                        __dom.push(__target.getElementById(selector.match(__idSelector)[1]));

                    } else if (!!__classSelector.test(selector)) {

                        var __className = selector.match(__classSelector)[1];

                        if (document.getElementsByClassName) {

                            __dom = [].slice.call(__target.getElementsByClassName(__className));
                        } else {

                            // 获取页面所有element
                            var __elems = __target.getElementsByTagName('*');

                            for (var i = 0, l = __elems.length; i < l; i++) {

                                if ((' ' + __elems[i].className + ' ').replace(__blank, ' ').indexOf(' ' + __className + ' ') >= 0) {
                                    __dom.push(__elems[i]);
                                }
                            }
                        }

                    } else {
                        throw new Error('Selector parameter error');
                    }
                }
            } else if (!!selector.nodeType) {
                __dom.push(selector);
            } else {
                throw new Error('Selector parameter error');
            }

            // 构造链式dom操作工具函数
            if (__dom.length != 0) {
                this._$dom = __dom;
                this._$length = __dom.length;
                this._$selector = selector;
                return this;
            } else {
                throw new Error('Selector no such element');
            }
        },

        // 返回特定位置的element 从0开始
        // @param  {Number}   arg0 - dom序号
        // @return {Object}        - this 链式调用
        _$eq: function (number) {
            if (this._$dom[number]) {
                this._$dom = [this._$dom[number]];
                this._$length = 1;
            }
            return this;
        },

        // 父节点下查找子节点
        // @param  {String}   arg0 - selector
        // @return {Object}        - this 链式调用
        _$find: function (selector) {

            this.__init(selector, this._$dom[0]);
            return this;
        },

        // 设定或查询相应的attribute属性
        // @param  {String}   arg0 - 属性
        // @param  {String}   arg1 - 属性值
        // @param  {Number}   arg1 - 属性值
        // @param  {Object}        - this 链式调用
        _$attr: function (key, value) {

            // 修改
            if (value != undefined) {

                this._$dom[0].setAttribute(key, value);
                return this;

            // 无参数 返回
            } else {

                return this._$dom[0].getAttribute(key);
            }
        },

        // 设定或查询相应的style属性
        // 简易id class选择器
        // @param  {String}   arg0 - 属性
        // @param  {String}   arg1 - 属性值N
        // @param  {Number}   arg1 - 属性值
        _$style: function (key, value) {

            // 设定style属性
            if (!!key && !!value) {

                // 枚举style属性中无需单位的数字属性
                var cssNumber = {
                    'columnCount': true,
                    'fillOpacity': true,
                    'fontWeight': true,
                    'opacity': true,
                    'zIndex': true,
                    'zoom': true
                };

                this._$dom._$forEach(function (element) {

                    // 过滤number是否需要单位
                    if (typeof value === 'number' && !cssNumber[key]) {
                        value += 'px';
                    }

                    try {
                        element.style[key] = value;
                    } catch (e) {
                        return false;
                    }
                })

            // 获取style属性
            } else if (!!key) {

                if (key != 'width' && key != 'height') {

                    if (document.documentElement.currentStyle) {
                        return this._$dom[0].currentStyle[key];
                    } else {
                        return global.getComputedStyle(this._$dom[0], null)[key];
                    }
                } else {
                    return key == 'width' ? this._$dom[0].offsetWidth : this._$dom[0].offsetHeight;
                }
            }
        },

        // 判断时候含有类
        // @param  {String}   arg0 - 类名 (仅支持一个类名操作)
        // @return {Boolean}       - 是否含有类
        _$hasClass: function (selector) {

            // 匹配的classname
            var __className = ' ' + selector + ' ';

            for (var i = 0, l = this._$length; i < l; i++) {

                if ((' ' + this._$dom[i].className + ' ').replace(__blank, ' ').indexOf(__className) >= 0) {
                    return true;
                }
            }
            return false;
        },

        // 增加类属性
        // @param  {String}   arg0 - 类名 (支持多个加入 以空格隔开)
        // @return {Object}        - this 链式调用
        _$addClass: function (value) {

            if (value && _$$p._$isString(value)) {

                this._$dom._$forEach(function (elem) {

                    // 标记加入属性的个数
                    var __addNum = 0;

                    // 加入的单个属性
                    var __addone;

                    // 获取输入的多个属性
                    var __addClasses = value.match(/\S+/g) || [];

                    // 目标属性
                    var __className = elem.className ? ( ' ' + elem.className + ' ' ).replace(__blank, ' ') : ' ';

                    while ((__addone = __addClasses[__addNum++])) {
                        if (__className.indexOf(' ' + __addone + ' ') < 0) {
                            __className += __addone + ' ';
                        }
                    }
                    elem.className = __className._$trim();
                })
            }
            return this;
        },

        // 去除类属性
        // @param  {String}   arg0 - 类名 (支持多个去除 以空格隔开)
        // @return {Object}        - this 链式调用
        _$removeClass: function (value) {

            if (value && _$$p._$isString(value)) {

                this._$dom._$forEach(function (elem) {

                    // 标记去除属性的个数
                    var __removeNum = 0;

                    // 去除的单个属性
                    var __removeOne;

                    // 获取去除的多个属性
                    var __removeClasses = value.match(/\S+/g) || [];

                    // 目标属性
                    var __className = elem.className ? ( ' ' + elem.className + ' ' ).replace(__blank, ' ') : ' ';

                    while ((__removeOne = __removeClasses[__removeNum++])) {

                        while (__className.indexOf(' ' + __removeOne + ' ') >= 0) {
                            __className = __className.replace(' ' + __removeOne + ' ', ' ');
                        }

                    }
                    elem.className = __className._$trim();
                })
            }
            return this;
        },


        // 当前元素有目标类则去除该类 反之亦然
        // @param  {String}   arg0 - 类名 (仅支持一个类名操作)
        // @return {Object}        - this 链式调用
        _$toggleClass: function (value) {

            if (value && _$$p._$isString(value)) {

                this._$dom._$forEach(function (elem) {

                    // 标记新的_$$p对象
                    var __this = {};

                    // 注入新的dom元素
                    __this._$dom = [elem];

                    // 标记内部dom长度
                    __this._$length = 1;

                    if (this._$hasClass.call(__this, value)) {

                        this._$removeClass.call(__this, value);
                    } else {

                        this._$addClass.call(__this, value);
                    }

                }._$bind(this))
            }
            return this;
        },

        // 添加事件
        // @param  {String}   arg0 - 事件名
        // @param  {Function  arg1 - callback
        _$on: function (event, callback) {

            if (_$$p._$isFunction(callback)) {

                var __event = event.indexOf('on') ? 'on' + event : event;
                this._$dom._$forEach(function (elem) {

                    if (__event in elem) {

                        elem[__event] = callback;
                    } else {
                        throw new Error('On no such event: ' + event);
                    }
                })
            } else {
                throw new Error('On parameter error');
            }
        },

        //
        //html模板注入 (注:当前仅支持1个元素注入1次)
        //
        // ```javascript
        // _$$p(xxx)._$setTpl({
        //     'tpl1':'<div>我是模板1</div>',
        //     'tpl2':'<div>我是模板2</div>'
        // }).setHash({
        //     'tpl1':'/tpl1',
        //     'tpl2':'/tpl2'
        // })
        // ```
        //
        // 会根据不同的hash值变化注入不同的模板 模板名称必须一一对应！ (请注意调用顺序)
        // 当模板元素被改变  请使用_$updateTpl更新模板
        //
        // ```javascript
        // _$$p(xxx)._$dom[0].innerHTML = '模板变化了';
        // _$$p(xxx)._$updateTpl();
        // ```
        //
        // 更新后的模板会储存在当前的hash下
        //
        _$setTpl: function (catalogue) {

            if (_$$p._$isObject(catalogue)) {

                // 添加template缓存
                for (var pos in catalogue) {
                    __tplData(this._$dom[0], pos, catalogue[pos]);
                }

            } else {
                throw new Error('SetTpl parameter error');
            }
            return this;
        },

        _$setHash: function (catalogue, callback) {

            if (_$$p._$isObject(catalogue)) {

                var __hashArray = [], __posArray = [], __dom = this._$dom[0];

                // 为hash缓存中添加地址
                for (var pos in catalogue) {
                    var __hash = catalogue[pos];
                    if (__hash.indexOf('#') < 0) {
                        __hash = '#' + __hash;
                    }
                    __posArray.push(pos);
                    __hashArray.push(__hash);
                    __hashReservoir[pos] = __hash;
                }

                // 对相应hash值渲染不同template
                var __action = function () {

                    for (var pos in __hashReservoir) {

                        var __initPageHash = false;

                        // 检测页面hash是否有对应模板缓存
                        if (_$$p._$pageHash() == __hashReservoir[pos]) {

                            for (var uid in __tplCache) {

                                // 获取当前template
                                var __tpl = __tplCache[uid][pos];

                                if (!!__tpl) {

                                    __initPageHash = true;
                                    __dom.innerHTML = __tpl;
                                    if (callback && _$$p._$isFunction(callback)) callback(uid);
                                }
                            }
                            break;
                        }
                    }

                    // 页面的hash值初始化
                    if (!__initPageHash) {

                        _$$p._$pageHash(__hashArray[0]);

                        for (var uid in __tplCache) {

                            // 获取当前template
                            var __tpl = __tplCache[uid][__posArray[0]];

                            if (!!__tpl) {

                                __initPageHash = true;
                                __dom.innerHTML = __tpl;
                                if (callback && _$$p._$isFunction(callback)) callback(uid);
                            }
                        }
                    }

                    // hash值变化检测
                    __hashChange(arguments.callee);

                }

                // 页面初始化检测hash
                __action();

            } else {
                throw new Error('SetHash parameter error');
            }
            return this;
        },

        _$updateTpl: function () {
            if (!!this._$dom[0][__expando]) {
                for (var pos in __hashReservoir) {

                    // 检测页面hash是否有对应模板缓存
                    if (_$$p._$pageHash() == __hashReservoir[pos]) {

                        for (var uid in __tplCache) {

                            // 更新相应模板
                            if (!!__tplCache[uid][pos]) {
                                __tplCache[uid][pos] = this._$dom[0].innerHTML;
                            }
                        }
                        break;
                    }
                }
            } else {
                throw new Error('UpdateTpl no such template');
            }
        }
    }

    _$$p.__fn.__init.prototype = _$$p.__fn;

    global._$$p = _$$p;

})(window, undefined)